/**
    @author: ChenLin
    @date: 2022/11/11 //todo http视频处理服务
**/

package service

import (
	"../handler"
	"errors"
	flamigoUtils "flamingo/utils"
	"fmt"
	XXProto "go/XXProtocols"
	"io"
	"log"
	"math/rand"
	"mime/multipart"
	"os"
	"strconv"
	"strings"
	"time"
)

type HttpVideoProcessService struct {
	FlamingoDFS   *handler.FlamingoDFS
	Mysql         *handler.VideoProcessMysql
	Ch            chan handler.VideoProcess
	MultiDownload *handler.MultiThreadDownload
}

var Root string = "/tmp"
var VideoDir string = Root + "/video/"
var ImageDir string = Root + "/image/"

func NewHttpVideoProcessService(ch chan handler.VideoProcess) *HttpVideoProcessService {
	service := &HttpVideoProcessService{}
	service.FlamingoDFS = handler.NewFlamingoDFS()
	service.Mysql = handler.NewMysql()
	service.Ch = ch
	service.MultiDownload = handler.MultiThreadInit(8, 0, 3, "", "md5", "")
	return service
}

func (v *HttpVideoProcessService) DownloadVideo(videoUrl, dirUrl string) (string, error) {
	log.Println("videoUrl:", videoUrl)
	log.Println("dirUrl:", dirUrl)
	url, err := v.MultiDownload.HttpGetVideoByUrlV1(videoUrl, dirUrl)
	log.Println("DownloadVideo url:", url)
	if err != nil {
		return "", err
	}
	return url, nil
}

func (v *HttpVideoProcessService) DownloadVideoByFile(file multipart.File, dirUrl string) (string, error) {
	url, err := handler.DownloadVideoByFileFrom(file, dirUrl)
	if err != nil {
		return url, err
	}
	return url, nil
}

func (v *HttpVideoProcessService) DownloadVideoByBytes(read io.ReadCloser, dirUrl string) (string, error) {
	url, err := handler.DownloadVideoByBytes(read, dirUrl)
	if err != nil {
		return url, err
	}
	return url, nil
}

func (v *HttpVideoProcessService) ScreenShot(fileUrl, imageUrl string) (string, error) {
	imageUrl = imageUrl + flamigoUtils.MD5(time.Now().String()+strconv.Itoa(rand.Int())) + ".jpg"
	log.Println("ScreenShot imageUrl:", imageUrl)
	log.Println("ScreenShot fileUrl:", fileUrl)
	err := handler.FfmpegScreenShot(fileUrl, 1, imageUrl)
	if err != nil {
		return "", err
	}
	return imageUrl, nil
}

func (v *HttpVideoProcessService) UploadImageToDFS(imageUrl, uploadName string) (dfs *handler.UploadResult, err error) {
	log.Println("UploadImageToDFS image:", imageUrl)
	log.Println("UploadImageToDFS uploadName:", uploadName)
	file, err := os.OpenFile(imageUrl, os.O_RDONLY, 0)
	if err != nil {
		log.Println(err)
		//file, err = os.Open("/tmp/image/61f300367d1dc7778ee4ad5a45207c24.jpg")
	}
	defer file.Close()
	stat, err := file.Stat()
	if err != nil {
		log.Println(err)
	}
	fileSize := stat.Size()
	buffer := make([]byte, fileSize)
	file.Read(buffer)
	object := &XXProto.FileObject{FileBytes: buffer}
	md5 := v.FlamingoDFS.CheckImageExist(object)
	uploadData := handler.UploadData{Name: imageUrl[1:], Data: buffer, FileType: int32(XXProto.FileType_FT_JPG), Md5: md5}
	dfs = v.FlamingoDFS.UploadFileToDFS(uploadData, uploadName[1:], "", "", "视频截图", false, true, true)
	if dfs != nil {
		log.Println("UploadImageToDFS img dfs:", dfs)
		err = nil
	} else {
		err = errors.New("UploadImageToDFS img dfs fail")
	}
	return
}

func (v *HttpVideoProcessService) VideoCompress(videoUrlIn, videoUrlOut string) (string, error) {
	log.Println("VideoCompress videoUrlIn:", videoUrlIn)
	log.Println("VideoCompress videoUrlOut:", videoUrlOut)
	videoUrlOut = videoUrlOut + flamigoUtils.MD5(time.Now().String()+strconv.Itoa(rand.Int())) + ".mp4"
	err := handler.FfmpegCompressVideoFast(videoUrlIn, 28, videoUrlOut)
	if err != nil {
		log.Println("VideoCompress err:", err)
		return "", err
	}
	return videoUrlOut, nil
}

func (v *HttpVideoProcessService) UploadVideoToDFS(videoUrl, UploadName string) (dfs *handler.UploadResult, err error) {
	log.Println("UploadVideoToDFS videoUrl:", videoUrl)
	log.Println("UploadVideoToDFS UploadName:", UploadName)
	file, err := os.OpenFile(videoUrl, os.O_RDONLY, 0)
	if err != nil {
		log.Println("UploadVideoToDFS Open err:", err)
		//file, err = os.Open("/tmp/video/fafe1bdd757c7c36bec6876f0764a1b3.mp4")
	}
	defer file.Close()
	stat, err := file.Stat()
	if err != nil {
		log.Println("UploadVideoToDFS stat err:", err)
	}
	fileSize := stat.Size()
	buffer := make([]byte, fileSize)
	file.Read(buffer)
	object := &XXProto.FileObject{FileBytes: buffer}
	md5 := v.FlamingoDFS.CheckImageExist(object)
	uploadData := handler.UploadData{Name: videoUrl[1:], Data: buffer, FileType: int32(XXProto.FileType_FT_JPG), Md5: md5}
	dfs = v.FlamingoDFS.UploadFileToDFS(uploadData, UploadName[1:], "", "", "视频文件", false, true, true)
	if dfs != nil {
		log.Println("UploadVideoToDFS dfs:", dfs)
		err = nil
	} else {
		err = errors.New("UploadVideoToDFS video dfs fail")
	}
	return
}

func (v *HttpVideoProcessService) UpDateVideoToDFS(videoUrl, UploadName string) (dfs *handler.UploadResult, err error) {
	log.Println("UpDateVideoToDFS videoUrl:", videoUrl)
	log.Println("UpDateVideoToDFS UploadName:", UploadName)
	file, err := os.OpenFile(videoUrl, os.O_RDONLY, 0)
	if err != nil {
		log.Println("UpDateVideoToDFS Open err:", err)
		//file, err = os.Open("/tmp/video/fafe1bdd757c7c36bec6876f0764a1b3.mp4")
	}
	defer file.Close()
	stat, err := file.Stat()
	if err != nil {
		log.Println("UpDateVideoToDFS stat err:", err)
	}
	fileSize := stat.Size()
	buffer := make([]byte, fileSize)
	file.Read(buffer)
	object := &XXProto.FileObject{FileBytes: buffer}
	md5 := v.FlamingoDFS.CheckImageExist(object)
	uploadData := handler.UploadData{Name: videoUrl[1:], Data: buffer, FileType: int32(XXProto.FileType_FT_JPG), Md5: md5}
	dfs = v.FlamingoDFS.UpdateFileToDFS(uploadData, UploadName, "", "", "视频文件", false, true, true)
	if dfs != nil {
		log.Println("UpDateVideoToDFS dfs:", dfs)
		err = nil
	} else {
		err = errors.New("UpDateVideoToDFS video dfs fail")
	}
	return
}

func (v *HttpVideoProcessService) TaskProducer(out handler.VideoProcess) {
	log.Println("goroutine len", len(v.Ch))
	v.Ch <- out
}

func (v *HttpVideoProcessService) TaskConsumer() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("panic err is %s\n", err)
			go v.TaskConsumer()
		}
	}()
	for {
		val, ok := <-v.Ch
		if ok {
			log.Println(val)
			id, rep, err := v.DoTask(val)
			if err != nil {
				log.Println(err)
				//doFailResponse
				err = v.doFailResponse(val, rep)
				if err != nil {
					if id != 0 {
						go v.UpdateProcess(id, 77, rep.Result.(handler.VideoProcess)) //77回调接口失败
					}
				}
				if id != 0 {
					go v.UpdateProcess(id, 7, rep.Result.(handler.VideoProcess)) //7回调接口成功
				}
			} else {
				//doSuccessResponse
				err = v.doSuccessResponse(val, rep)
				if err != nil {
					if id != 0 {
						go v.UpdateProcess(id, 77, rep.Result.(handler.VideoProcess)) //77回调接口失败
					}
				}
				if id != 0 {
					go v.UpdateProcess(id, 7, rep.Result.(handler.VideoProcess)) //7回调接口成功
				}
			}
		} else {

		}
	}
}

func (v *HttpVideoProcessService) DoTask(task handler.VideoProcess) (int32, handler.Rep, error) {
	if task.Url != "" {
		videoUrl, err := v.DownloadVideo(task.Url, VideoDir)
		task.DownloadUrl = videoUrl
		if err != nil {
			log.Println(err)
			go v.UpdateProcess(task.TaskId, 11, task) //11视频下载失败
			if err != nil {
				response := handler.ParseResponse(500, "入库失败", task)
				return 0, response, err
			}
			response := handler.ParseResponse(402, "下载视频失败", task)
			return task.TaskId, response, err
		}
	}
	go v.UpdateProcess(task.TaskId, 1, task) //1视频已下载
	screenUrl, err := v.ScreenShot(task.DownloadUrl, ImageDir)
	task.ScreenUrl = screenUrl
	if err != nil {
		log.Println(err)
		go v.UpdateProcess(task.TaskId, 22, task) //22视频截图失败
		response := handler.ParseResponse(403, "视频截图失败", task)
		return task.TaskId, response, err
	}
	go v.UpdateProcess(task.TaskId, 2, task) //2视频已截图
	dfsImage, err := v.UploadImageToDFS(task.ScreenUrl, task.ScreenUrl)
	task.ImageUrl = dfsImage.Message
	if err != nil {
		log.Println(err)
		go v.UpdateProcess(task.TaskId, 33, task) //33视频截图上传dfs失败
		response := handler.ParseResponse(404, "截图上传DFS失败", task)
		return task.TaskId, response, err
	}
	go v.UpdateProcess(task.TaskId, 3, task) //3视频截图已上传dfs
	if task.Type == 1 {                      //只截图，不处理视频
		response := handler.ParseResponse(200, "视频截图成功", task)
		err = handler.RemoveFile(response)
		if err != nil {
			go v.UpdateProcess(task.TaskId, 66, task) //66文件删除失败
			log.Println("文件删除失败")
		} else {
			go v.UpdateProcess(task.TaskId, 8, task) //6文件已删除
		}
		return task.TaskId, response, err
	}
	compressUrl, err := v.VideoCompress(task.DownloadUrl, VideoDir)
	task.CompressUrl = compressUrl
	if err != nil {
		log.Println(err)
		go v.UpdateProcess(task.TaskId, 44, task) //44视频压缩失败
		response := handler.ParseResponse(405, "视频压缩失败", task)
		return task.TaskId, response, err
	}
	go v.UpdateProcess(task.TaskId, 4, task) //4视频已压缩
	if task.Type == 2 {
		dfsVideo, err := v.UploadVideoToDFS(task.CompressUrl, task.CompressUrl)
		task.VideoUrl = dfsVideo.Message
		if err != nil {
			log.Println(err)
			go v.UpdateProcess(task.TaskId, 55, task) //55压缩视频上传dfs失败
			response := handler.ParseResponse(406, "视频上传失败", task)
			return task.TaskId, response, err
		}
	} else {
		index := strings.LastIndex(task.Url, ".cn/")
		log.Println("task.url:", task.Url, "task.url[index+4:]:", task.Url[index+4:])
		dfsVideo, err := v.UpDateVideoToDFS(task.CompressUrl, task.Url[index+4:])
		task.VideoUrl = dfsVideo.Message
		if err != nil {
			log.Println(err)
			go v.UpdateProcess(task.TaskId, 55, task) //55压缩视频上传dfs失败
			response := handler.ParseResponse(406, "视频上传失败", task)
			return task.TaskId, response, err
		}
	}
	go v.UpdateProcess(task.TaskId, 5, task) //5压缩视频已上传dfs
	response := handler.ParseResponse(200, "视频压缩成功", task)
	err = handler.RemoveFile(response)
	if err != nil {
		go v.UpdateProcess(task.TaskId, 66, task) //66文件删除失败
		log.Println("文件删除失败")
	} else {
		go v.UpdateProcess(task.TaskId, 8, task) //6文件已删除
	}
	return task.TaskId, response, nil
}

func (v *HttpVideoProcessService) doFailResponse(task handler.VideoProcess, rep handler.Rep) error {
	err := handler.ClientPost(task.RedirectUrl, rep)
	return err
}

func (v *HttpVideoProcessService) doSuccessResponse(task handler.VideoProcess, rep handler.Rep) error {
	err := handler.ClientPost(task.RedirectUrl, rep)
	return err
}

func (v *HttpVideoProcessService) InsertAndSetStatus(status int32, process handler.VideoProcess) (int32, error) {
	process.Status = int(status)
	id, err := v.Mysql.InsertVideoProcess(process)
	log.Println("insert id:", id)
	if err != nil {
		log.Println("insertVideoProcess err:", err)
	}
	return id, nil
}

func (v *HttpVideoProcessService) UpdateProcess(taskId, status int32, process handler.VideoProcess) {
	process.Status = int(status)
	process.TaskId = taskId
	err := v.Mysql.UpdateVideoProcess(process)
	if err != nil {
		log.Println("updateVideoProcess err:", err)
	}
}
